#include "lua.h"
#include "lauxlib.h"
#include "cm_fs.h"
#include <string.h>
#include <stdlib.h> // Required for malloc and free

#define LUA_FILE_HANDLE "FILE*"

typedef struct {
    int32_t fd;
} lua_file_handle;

// Lua: f = fs.open(filename, mode)
static int l_fs_open(lua_State *L) {
    const char *filename = luaL_checkstring(L, 1);
    const char *mode = luaL_checkstring(L, 2);

    int flag = 0;
    if (strcmp(mode, "rb") == 0) flag = CM_FS_RB;
    else if (strcmp(mode, "wb") == 0) flag = CM_FS_WB;
    else if (strcmp(mode, "ab") == 0) flag = CM_FS_AB;
    else if (strcmp(mode, "wb+") == 0) flag = CM_FS_WBPLUS;
    else if (strcmp(mode, "ab+") == 0) flag = CM_FS_ABPLUS;
    else if (strcmp(mode, "rb+") == 0) flag = CM_FS_RBPLUS;
    else return luaL_argerror(L, 2, "invalid mode");

    int32_t fd = cm_fs_open(filename, flag);

    if (fd < 0) {
        lua_pushnil(L);
        return 1;
    }

    lua_file_handle *handle = (lua_file_handle *)lua_newuserdata(L, sizeof(lua_file_handle));
    handle->fd = fd;
    luaL_setmetatable(L, LUA_FILE_HANDLE);
    return 1;
}

// file:close()
static int l_file_close(lua_State *L) {
    lua_file_handle *handle = (lua_file_handle *)luaL_checkudata(L, 1, LUA_FILE_HANDLE);
    if (handle->fd >= 0) {
        cm_fs_close(handle->fd);
        handle->fd = -1; // Mark as closed
    }
    return 0;
}

// file:write(content)
static int l_file_write(lua_State *L) {
    lua_file_handle *handle = (lua_file_handle *)luaL_checkudata(L, 1, LUA_FILE_HANDLE);
    size_t len;
    const char *content = luaL_checklstring(L, 2, &len);

    int32_t bytes_written = cm_fs_write(handle->fd, content, len);
    lua_pushinteger(L, bytes_written);
    return 1;
}

// file:read(bytes) -> string
static int l_file_read(lua_State *L) {
    lua_file_handle *handle = (lua_file_handle *)luaL_checkudata(L, 1, LUA_FILE_HANDLE);
    int bytes_to_read = luaL_checkinteger(L, 2);

    if (bytes_to_read <= 0) {
        lua_pushstring(L, "");
        return 1;
    }

    // Allocate a buffer from the heap
    char *buffer = (char *)malloc(bytes_to_read);
    if (!buffer) {
        return luaL_error(L, "malloc failed for read buffer");
    }

    int32_t bytes_read = cm_fs_read(handle->fd, buffer, bytes_to_read);

    if (bytes_read >= 0) {
        lua_pushlstring(L, buffer, bytes_read);
    } else {
        lua_pushnil(L);
    }

    free(buffer); // Free the buffer
    return 1;
}

// Lua: fs.remove(filename)
static int l_fs_remove(lua_State *L) {
    const char *filename = luaL_checkstring(L, 1);
    int ret = cm_fs_delete(filename);
    lua_pushboolean(L, ret == 0);
    return 1;
}

// Lua: fs.exists(filename)
static int l_fs_exists(lua_State *L) {
    const char *filename = luaL_checkstring(L, 1);
    int ret = cm_fs_exist(filename);
    lua_pushboolean(L, ret == 1);
    return 1;
}

// Lua: fs.rename(old, new)
static int l_fs_rename(lua_State *L) {
    const char *old_name = luaL_checkstring(L, 1);
    const char *new_name = luaL_checkstring(L, 2);
    int ret = cm_fs_move(old_name, new_name);
    lua_pushboolean(L, ret == 0);
    return 1;
}


static const luaL_Reg file_methods[] = {
    {"close", l_file_close},
    {"write", l_file_write},
    {"read", l_file_read},
    {"__gc", l_file_close}, // Automatically close on garbage collection
    {NULL, NULL}
};

static const luaL_Reg fs_lib[] = {
    {"open", l_fs_open},
    {"remove", l_fs_remove},
    {"exists", l_fs_exists},
    {"rename", l_fs_rename},
    {NULL, NULL}
};

int luaopen_fs(lua_State *L) {
    luaL_newmetatable(L, LUA_FILE_HANDLE);
    lua_pushvalue(L, -1);
    lua_setfield(L, -2, "__index");
    luaL_setfuncs(L, file_methods, 0);

    luaL_newlib(L, fs_lib);
    return 1;
}
